//小车发送数据开门，返回值告知可以走了，小车走后发送数据，告知可以关门
#include "tcp.h"
// #include "wifi.h"

const char *TCP_TAG = "TCP SERVER MSG"; // 定义日志标签，用于标识TCP服务器相关的日志信息
char *broke = "-1";//数据传输错误
 char *op = "0";//接收到小车数据开门
char *cldo = "1";//接收到小车数据关门
int data = 0;//1是表示开门，2是表示关门
int listen_sock = -1;//监听套接字
/**
 * @brief 处理TCP数据收发的核心函数
 * @param sock 已建立连接的客户端套接字
 *
 * 功能说明：
 * 1. 接收客户端发送的数据,小车开门
 * 2. 解析数据内容，识别特定指令（如开门指令）
 * 3. 将接收到的数据原样回传给客户端（回显功能）
 */
void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[128];
    bool ret;

    do
    {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0)
        {
            ESP_LOGE(TCP_TAG, "Error occurred during receiving: errno %d", errno);
            send(sock, broke, strlen(broke), 0);
            break; // 添加break以退出循环
        }
        else if (len == 0)
        {
            ESP_LOGW(TCP_TAG, "Connection closed");
            break;
        }
        else
        {
            rx_buffer[len] = 0; // 在数据末尾添加字符串结束符，确保可以当作字符串处理
            ESP_LOGI(TCP_TAG, "Received %d bytes: %s", len, rx_buffer);
            if (len >= 0 && rx_buffer[0] == '1')
            {
                printf("通过wifi开门\r\n");
                data = 1;
                control(data);
                vTaskDelay(15000 / portTICK_PERIOD_MS);//延时5秒
                ret = judge();
                if(ret == false) {
                    op="-1";
                    printf("开门失败，可能到位开关有问题\r\n");
                    send(sock, op, strlen(op), data);
                }else {//判断成功
                    op="1";
                    printf("开门成功\r\n");
                    send(sock, op, strlen(op), data);
                }
                
                printf("返回开门状态开门：%s\r\n", ret ? "成功" : "失败");
                gpio_init();
            }
             else if (len >= 0 && rx_buffer[0] == '0')
            {
                printf("通过wifi关门\r\n");
                data = 2;
                control(data);
                vTaskDelay(15000 / portTICK_PERIOD_MS);//延时5秒
                ret = judge();
                if(ret == false) {
                    op="-1";
                    printf("关门失败，可能到位开关有问题\r\n");
                    send(sock, op, strlen(op), -1);
                }else {//判断成功
                    op="0";
                    printf("关门成功\r\n");
                    send(sock, op, strlen(op), 0);
                }
                printf("返回关门状态开门：%s\r\n", ret ? "成功" : "失败");
                gpio_init();
            }
        }
    } while (len > 0);
}

//获取当前ip地址
esp_err_t get_sta_ip_address(struct in_addr *ip_addr)
{
    esp_netif_ip_info_t ip_info;
    esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
    
    if (sta_netif && esp_netif_get_ip_info(sta_netif, &ip_info) == ESP_OK) {
        ip_addr->s_addr = ip_info.ip.addr;
        return ESP_OK;
    }
    return ESP_FAIL;
}
// 小车

/**
 * @brief TCP服务器主任务函数
 * @param pvParameters 任务参数，这里用来传递地址族信息
 *
 * 功能说明：
 * 1. 创建TCP服务器套接字
 * 2. 绑定监听地址和端口
 * 3. 等待并处理客户端连接
 * 4. 为每个连接的客户端提供数据服务
 */


void tcp_server_task()
{
    char addr_str[128];
    int addr_family = AF_INET;
    int ip_protocol = 0;
    int keepAlive = 1;
    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    struct sockaddr_storage dest_addr;
    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        
    // 获取STA接口的IP地址并绑定到该地址
    struct in_addr sta_ip;
    if (get_sta_ip_address(&sta_ip) == ESP_OK) {
        dest_addr_ip4->sin_addr.s_addr = sta_ip.s_addr;
        ESP_LOGI(TCP_TAG, "Binding to STA IP: %s", inet_ntoa(sta_ip));
    } else {
        // 如果无法获取IP，则绑定到所有接口
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        ESP_LOGI(TCP_TAG, "Binding to all interfaces (INADDR_ANY)");
    }
    
    dest_addr_ip4->sin_family = AF_INET;
    dest_addr_ip4->sin_port = htons(PORT);
    ip_protocol = IPPROTO_IP;
    // 创建TCP监听套接字
    // 参数说明：addr_family(地址族), SOCK_STREAM(TCP流式套接字), ip_protocol(IP协议)
    listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0)
    {
        // 套接字创建失败，记录错误日志并删除任务
        ESP_LOGE(TCP_TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    // 设置套接字选项，允许地址重用（避免程序重启时出现地址已被占用的错误）
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    ESP_LOGI(TCP_TAG, "Socket created");
    // 将套接字绑定到指定地址和端口
    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0)
    {
        ESP_LOGE(TCP_TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TCP_TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TCP_TAG, "Socket bound, port %d", PORT);
    // 开始监听连接请求，设置最大等待连接队列为1
    err = listen(listen_sock, 1);
    if (err != 0)
    {
        ESP_LOGE(TCP_TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1)
    {
        ESP_LOGI(TCP_TAG, "Socket listening");
        // 准备接收客户端连接的地址信息结构
        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t addr_len = sizeof(source_addr);
        // 等待并接受客户端连接请求（阻塞调用），等待客户端连接
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0)
        {
            ESP_LOGE(TCP_TAG, "Unable to accept connection: errno %d", errno);
             // 继续监听而不是break，避免服务器退出
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }
        // 将客户端IP地址转换为字符串格式便于显示
        if (source_addr.ss_family == PF_INET)
        { // IPv4地址转换
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str));
        }
        ESP_LOGI(TCP_TAG, "Socket accepted ip address: %s", addr_str);
        // 调用数据处理函数处理与客户端的数据交互
        do_retransmit(sock);
        // 关闭客户端连接并释放资源
        // shutdown(sock, 0);
        // close(sock);
    }

    // 错误清理标签：当发生错误时跳转到这里进行资源清理
CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}